import { WebSocketServer } from 'ws';
import fs from 'fs';
import path from 'path';
import os from 'os';
import { PORT, SAMPLE_RATE, CHANNELS, BIT_DEPTH, SEGMENT_FRAMES, OPUS_CLOCK_RATE, OPUS_FRAME_DURATION_MS } from './config.js';

function createOpusHead(sampleRate, channels) {
    const buffer = new ArrayBuffer(19);
    const view = new DataView(buffer);
    const uint8 = new Uint8Array(buffer);
    const head = [0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64];
    uint8.set(head, 0);
    uint8[8] = 1; // 版本
    uint8[9] = channels;
    view.setUint16(10, 0, true); // 预跳过
    view.setUint32(12, sampleRate, true); // 采样率
    view.setUint16(16, 0, true); // 增益
    uint8[18] = 0; // 通道映射
    return new Uint8Array(buffer);
}

function createOpusTags() {
    const buffer = new ArrayBuffer(16);
    const view = new DataView(buffer);
    const uint8 = new Uint8Array(buffer);
    const tags = [0x4F, 0x70, 0x75, 0x73, 0x54, 0x61, 0x67, 0x73];
    uint8.set(tags, 0);
    view.setUint32(8, 0, true); // 供应商长度
    view.setUint32(12, 0, true); // 用户注释列表长度
    return new Uint8Array(buffer);
}

let crcTable = null;
function initCrcTable() {
    if (crcTable) return;
    crcTable = new Uint32Array(256);
    for (let i = 0; i < 256; i++) {
        let c = i << 24;
        for (let j = 0; j < 8; j++) {
            if (c & 0x80000000) {
                c = ((c << 1) ^ 0x04C11DB7) >>> 0;
            } else {
                c = (c << 1) >>> 0;
            }
        }
        crcTable[i] = c >>> 0;
    }
}

function calculateOggCrc(page) {
    initCrcTable();
    let crc = 0 >>> 0;
    for (let i = 0; i < page.length; i++) {
        if (i >= 22 && i < 26) {
            crc = ((crc << 8) ^ crcTable[(crc >>> 24) & 0xFF]) >>> 0;
        } else {
            crc = ((crc << 8) ^ crcTable[((crc >>> 24) ^ page[i]) & 0xFF]) >>> 0;
        }
    }
    return crc >>> 0;
}

function buildLacingForPacket(packetLength) {
    const lacing = [];
    let remaining = packetLength;
    while (remaining >= 255) {
        lacing.push(255);
        remaining -= 255;
    }
    lacing.push(remaining);
    return lacing;
}

function createOggPage(packets, streamSerial, pageSequence, granulePosition, isFirstPage, isLastPage) {
    const packetSizes = packets.map(p => p.length);
    const lacingValues = [];
    for (const size of packetSizes) {
        const lacing = buildLacingForPacket(size);
        for (const v of lacing) lacingValues.push(v);
    }
    const segmentCount = lacingValues.length;
    let totalPacketBytes = 0;
    for (const size of packetSizes) totalPacketBytes += size;
    const pageSize = 27 + segmentCount + totalPacketBytes;

    const page = new Uint8Array(pageSize);
    const view = new DataView(page.buffer);

    // OGG页头
    page[0] = 0x4F; page[1] = 0x67; page[2] = 0x67; page[3] = 0x53;
    page[4] = 0;
    let flags = 0;
    if (isFirstPage) flags |= 0x02;
    if (isLastPage) flags |= 0x04;
    page[5] = flags;

    // 粒度位置 (granule position)
    const low = granulePosition >>> 0;
    const high = Math.floor(granulePosition / 0x100000000) >>> 0;
    view.setUint32(6, low, true);
    view.setUint32(10, high, true);
    view.setUint32(14, streamSerial >>> 0, true);
    view.setUint32(18, pageSequence >>> 0, true);
    view.setUint32(22, 0, true); // CRC占位符
    page[26] = segmentCount;

    // 段表
    let offset = 27;
    for (const v of lacingValues) page[offset++] = v & 0xFF;
    
    // 数据包
    for (const pkt of packets) {
        page.set(pkt, offset);
        offset += pkt.length;
    }

    // 计算并设置CRC
    const crc = calculateOggCrc(page);
    view.setUint32(22, crc >>> 0, true);
    return page;
}

function createOggOpusFile(opusFrames, opusClockRate, channels, granuleStep) {
    const streamSerial = Math.floor(Math.random() * 0xFFFFFFFF) >>> 0;
    const pages = [];
    let seq = 0;
    let granule = 0;

    // 创建OPUS头页
    const head = createOpusHead(opusClockRate, channels);
    pages.push(createOggPage([head], streamSerial, seq++, 0, true, false));

    // 创建OPUS标签页
    const tags = createOpusTags();
    pages.push(createOggPage([tags], streamSerial, seq++, 0, false, false));

    // 创建OPUS数据页
    for (let i = 0; i < opusFrames.length; i++) {
        const frame = opusFrames[i];
        granule += granuleStep;
        const isLast = i === opusFrames.length - 1;
        pages.push(createOggPage([frame], streamSerial, seq++, granule, false, isLast));
    }

    // 合并所有页
    let total = 0;
    for (const p of pages) total += p.length;
    const out = Buffer.allocUnsafe(total);
    let off = 0;
    for (const p of pages) {
        out.set(p, off);
        off += p.length;
    }
    return out;
}

// 验证OPUS帧的有效性
function validateOpusFrame(frame) {
    if (!frame || frame.length < 1) return false;
    
    // 检查OPUS帧头
    const firstByte = frame[0];
    const config = (firstByte >> 3) & 0x1F;
    const stereo = (firstByte >> 2) & 0x01;
    
    // 基本验证
    if (config > 20) return false; // 无效的配置
    if (frame.length < 2) return false; // 帧太短
    
    return true;
}

// 清理OPUS帧数据
function cleanOpusFrame(frame) {
    if (!frame || frame.length === 0) return null;
    
    // 移除可能的填充字节
    let cleanFrame = frame;
    
    // 检查帧尾是否有无效数据
    if (frame.length > 1) {
        const lastByte = frame[frame.length - 1];
        if (lastByte === 0) {
            // 移除尾部的零字节
            let end = frame.length - 1;
            while (end > 0 && frame[end - 1] === 0) end--;
            cleanFrame = frame.slice(0, end);
        }
    }
    
    return cleanFrame.length > 0 ? cleanFrame : null;
}

class OpusWebSocketServer {
    constructor(options = {}) {
        this.port = options.port || PORT;
        this.wss = null;
        this.connections = new Map();
        this.fileSeq = 0;
        this.outputDir = options.outputDir || process.cwd();
        this.isShuttingDown = false;

        this.initWebSocketServer();
    }

    initWebSocketServer() {
        this.wss = new WebSocketServer({ port: this.port });

        this.wss.on('connection', (socket, request) => this.handleConnection(socket, request));

        this.wss.on('listening', () => {
            console.log(`OPUS WebSocket服务器已启动，监听端口 ${this.port}`);
            console.log('分片帧数=', SEGMENT_FRAMES);
            const nets = os.networkInterfaces();
            const addrs = [];
            for (const name of Object.keys(nets)) {
                for (const net of nets[name] || []) {
                    if (net && net.family === 'IPv4' && !net.internal) addrs.push(net.address);
                }
            }
            const list = addrs.length > 0 ? addrs : ['127.0.0.1'];
            console.log('可用访问地址:');
            for (const ip of list) {
                console.log(`- ws://${ip}:${this.port}`);
            }
        });
    }

    parseIdentifiers(request) {
        const origin = request?.headers?.origin || '';
        const urlStr = request?.url || '';

        const headers = request?.headers || {};
        const getHeader = (name) => headers[String(name).toLowerCase()] || '';

        let deviceId = getHeader('device-id') || '';
        let clientId = getHeader('client-id') || '';
        let channels = getHeader('channels') || '';
        let sampleRate = getHeader('sample-rate') || '';
        let segmentFrames = getHeader('segment-frames') || '';
        let bitDepth = getHeader('bit-depth') || '';
        let frameDuration = getHeader('frame-duration') || '';

        try {
            const base = `http://${request?.headers?.host || 'localhost'}`;
            const u = new URL(urlStr, base);
            const pickCaseInsensitive = (params, key) => {
                const target = String(key).toLowerCase();
                for (const [k, v] of params) {
                    if (String(k).toLowerCase() === target) return v;
                }
                return '';
            };
            if (!deviceId) deviceId = pickCaseInsensitive(u.searchParams, 'device-id');
            if (!clientId) clientId = pickCaseInsensitive(u.searchParams, 'client-id');
            if (!channels) channels = pickCaseInsensitive(u.searchParams, 'channels');
            if (!sampleRate) sampleRate = pickCaseInsensitive(u.searchParams, 'sample-rate');
            if (!segmentFrames) segmentFrames = pickCaseInsensitive(u.searchParams, 'segment-frames');
            if (!bitDepth) bitDepth = pickCaseInsensitive(u.searchParams, 'bit-depth');
            if (!frameDuration) frameDuration = pickCaseInsensitive(u.searchParams, 'frame-duration');
        } catch {}

        if (!deviceId) deviceId = 'unknown';
        if (!clientId) clientId = 'unknown';

        const toPositiveInt = (value, defaultValue) => {
            const n = parseInt(String(value || '').trim(), 10);
            return Number.isFinite(n) && n > 0 ? n : defaultValue;
        };

        const parsedChannels = toPositiveInt(channels, CHANNELS);
        const parsedSampleRate = toPositiveInt(sampleRate, SAMPLE_RATE);
        const parsedBitDepth = toPositiveInt(bitDepth, BIT_DEPTH);
        const parsedFrameDuration = toPositiveInt(frameDuration, OPUS_FRAME_DURATION_MS);
        const parsedSegmentFrames = toPositiveInt(segmentFrames, SEGMENT_FRAMES);

        return {
            origin,
            deviceId,
            clientId,
            channels: parsedChannels,
            sampleRate: parsedSampleRate,
            segmentFrames: parsedSegmentFrames,
            bitDepth: parsedBitDepth,
            frameDuration: parsedFrameDuration
        };
    }

    createConnectionInfo({ deviceId, clientId, channels, sampleRate, segmentFrames, bitDepth, frameDuration }) {
        const finalChannels = (typeof channels === 'number' && channels > 0) ? channels : CHANNELS;
        const finalSampleRate = (typeof sampleRate === 'number' && sampleRate > 0) ? sampleRate : SAMPLE_RATE;
        const finalSegmentFrames = (typeof segmentFrames === 'number' && segmentFrames > 0) ? segmentFrames : SEGMENT_FRAMES;
        const finalBitDepth = (typeof bitDepth === 'number' && bitDepth > 0) ? bitDepth : BIT_DEPTH;
        const finalFrameDuration = (typeof frameDuration === 'number' && frameDuration > 0) ? frameDuration : OPUS_FRAME_DURATION_MS;
        const frameSizeSamples = Math.round((OPUS_CLOCK_RATE * finalFrameDuration) / 1000);

        return {
            deviceId,
            clientId,
            channels: finalChannels,
            sampleRate: finalSampleRate,
            segmentFrames: finalSegmentFrames,
            bitDepth: finalBitDepth,
            frameDuration: frameSizeSamples,
            frames: [],
            frameCount: 0,
            tinyBinaryCount: 0,
            totalBytes: 0,
            messageCount: 0,
            validFrameCount: 0,
            invalidFrameCount: 0
        };
    }

    handleConnection(socket, request) {
        const identifiers = this.parseIdentifiers(request);
        console.log('identifiers=', identifiers);
        const { origin, deviceId, clientId, channels, sampleRate, segmentFrames, bitDepth, frame_duration } = identifiers;

        console.log('OPUS客户端已连接:', `${deviceId}:${clientId} from ${origin}`);

        const connectionInfo = this.createConnectionInfo({ deviceId, clientId, channels, sampleRate, segmentFrames, bitDepth, frame_duration });
        this.connections.set(socket, connectionInfo);

        console.log(`连接 ${deviceId}:${clientId} 使用分片帧数: ${connectionInfo.segmentFrames}`);

        socket.on('message', (data, isBinary) => {
            try {
                this.handleOpusData(socket, data, isBinary);
            } catch (error) {
                console.error('处理OPUS数据时出错:', error.message);
            }
        });

        socket.on('close', (code, reason) => {
            const reasonText = Buffer.isBuffer(reason) ? reason.toString() : String(reason || '');
            console.log('OPUS客户端已断开连接, 代码:', code, '原因:', reasonText);
            this.flushConnection(socket, true);
            this.connections.delete(socket);
        });

        socket.on('error', (error) => {
            console.error('WebSocket连接错误:', error.message);
            this.connections.delete(socket);
        });
    }

    handleOpusData(socket, data, isBinary) {
        const connectionInfo = this.connections.get(socket);
        if (!connectionInfo) return;

        try {
            if (isBinary) {
                const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
                connectionInfo.totalBytes += buf.length;
                connectionInfo.messageCount++;
                // 忽略极小二进制消息（可能是心跳/占位），避免刷屏
                if (buf.length <= 2) {
                    connectionInfo.tinyBinaryCount++;
                    return;
                }
                
                // 直接作为裸OPUS帧入队（已忽略极小占位包）
                const frame = new Uint8Array(buf);
                connectionInfo.frames.push(frame);
                connectionInfo.frameCount++;
                connectionInfo.validFrameCount++;
                
                console.log(`收到有效OPUS帧 ${connectionInfo.messageCount}: ${frame.length} 字节`);
                
                // 按帧数分片保存
                if (connectionInfo.frameCount >= connectionInfo.segmentFrames) {
                    this.flushConnection(socket, false);
                }
            } else {
                const text = Buffer.isBuffer(data) ? data.toString() : String(data);
                console.log('收到文本消息:', text);
            }
        } catch (error) {
            console.error('处理OPUS数据失败:', error);
        }
    }

    flushConnection(socket, final = false) {
        const connectionInfo = this.connections.get(socket);
        if (!connectionInfo || connectionInfo.frames.length === 0) return;

        const safe = (s) => String(s ?? 'unknown').replace(/[^a-zA-Z0-9_-]/g, '-');
        const name = `${safe(connectionInfo.deviceId)}_${safe(connectionInfo.clientId)}_${Date.now()}_${++this.fileSeq}.opus`;

        try {
            const framesToSave = connectionInfo.frames.splice(0);

            const now = new Date();
            const month = String(now.getMonth() + 1).padStart(2, '0');
            const day = String(now.getDate()).padStart(2, '0');
            const dateFolder = `${month}-${day}`;

            const dir = path.join(this.outputDir, safe(connectionInfo.deviceId), dateFolder);
            fs.mkdirSync(dir, { recursive: true });

            const filePath = path.join(dir, name);

            const granuleStep = Math.round((OPUS_CLOCK_RATE * OPUS_FRAME_DURATION_MS) / 1000);
            const oggBuffer = createOggOpusFile(
                framesToSave,
                OPUS_CLOCK_RATE,
                connectionInfo.channels,
                granuleStep
            );

            fs.writeFileSync(filePath, oggBuffer);
            console.log(`已保存OPUS文件: ${name}, 帧数: ${framesToSave.length}, 大小: ${oggBuffer.length} 字节, 有效帧: ${connectionInfo.validFrameCount}, 无效帧: ${connectionInfo.invalidFrameCount}`);
        } catch (e) {
            console.error('保存OPUS文件失败:', e?.message || e);
        }

        connectionInfo.frameCount = 0;
    }

    start() {
        console.log('OPUS WebSocket服务器已启动');
    }

    stop() {
        console.log('正在停止OPUS WebSocket服务器...');
        this.isShuttingDown = true;
        
        // 保存所有连接的剩余数据
        for (const [socket, connectionInfo] of this.connections) {
            this.flushConnection(socket, true);
        }
        
        if (this.wss) {
            this.wss.close();
        }
        
        console.log('OPUS WebSocket服务器已停止');
    }
}

export default OpusWebSocketServer;